/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.client.model;

import offset.nodes.client.virtual.model.jcr.PrintHandler;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.HashSet;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.helpers.AttributesImpl;


/**
 *
 * @author Walter Lütgenau
 */
public class XmlReader{
    ContentHandler contentHandler = null;
    HashSet<String> pureInlineElements = new HashSet<String>();
    
    /** Creates a new instance of XmlReader */
    public XmlReader() {
    }

    public org.xml.sax.ContentHandler getContentHandler() {
        return contentHandler;
    }

    public void addPureInlineElement(String qName) {
        pureInlineElements.add(qName);
    }
    
    protected int lengthOfLeadingSpace(StringBuffer buf ) {
        int i=0;
        for( i=0; i<buf.length(); i++) {
            switch( buf.charAt(i)) {
                case '\n':
                case '\r':
                case '\t':
                    continue;
                    
                default:
                    return i;
            }
        }
        
        return i;
    }
    
    class Name {
        String uri = "";
        String localname = "";
        String qname = "";
        
        public Name(String name) {
            if ( name.indexOf(":") < 0) {
                localname = name;
                qname = name;
            }
            else {
                String[] components = name.split(":");
                uri = "http://nodes/"+components[0];
                localname = components[1];
                qname = name;
            }
        }

        public String getLocalname() {
            return localname;
        }

        public String getQname() {
            return qname;
        }

        public String getUri() {
            return uri;
        }
    }
    
    public void parse(org.xml.sax.InputSource inputSource) throws java.io.IOException, org.xml.sax.SAXException {
        InputStreamReader in = new InputStreamReader( inputSource.getByteStream() );
        boolean isEndTag = false;
        int c = 0;
        StringBuffer buf = new StringBuffer();
        String elementName = null;
        String attributeName = null;
        final int AFTER_TAG_START = 1;
        final int IN_ATTRIBUTE_NAME = 2;
        final int BEFORE_ATTRIBUTE_VALUE_START = 3;
        final int IN_ATTRIBUTE_VALUE = 4;
        final int AFTER_TAG_END = 5;
        int state = AFTER_TAG_END;
        AttributesImpl atts = null;
        
        getContentHandler().startDocument();
        while( (c = in.read()) >= 0) {
            char ch = (char) c;
            switch( ch ) {
                case '<':
                    if ( state == AFTER_TAG_END ) {
                        if ( buf.length() > 0) {
                            char[] value = new char[buf.length()];
                            int len = lengthOfLeadingSpace(buf);
                            if ( len < buf.length()) {
                                buf.getChars(len, buf.length(), value, 0 );
                                getContentHandler().characters(value, 0, buf.length()-len );
                            }
                            buf = new StringBuffer();
                        }
                        state = AFTER_TAG_START;
                        isEndTag = false;
                        atts = new AttributesImpl();
                        continue;
                    }
                    
                case '/':
                    if ( state == AFTER_TAG_START || state == IN_ATTRIBUTE_NAME) {
                        isEndTag = true;
                        continue;
                    }
                    
                case '>':
                    if ( state != AFTER_TAG_END && state != IN_ATTRIBUTE_VALUE ) {
                        if ( elementName == null)
                            elementName = buf.toString();
                        buf = new StringBuffer();
                        Name name = new Name(elementName);
                        if ( !isEndTag || atts.getLength() > 0)
                            getContentHandler().startElement(name.getUri(), name.getLocalname(), name.getQname(), atts);
                        if ( isEndTag || pureInlineElements.contains(elementName))
                            getContentHandler().endElement(name.getUri(), name.getLocalname(), name.getQname());
                        elementName = null;
                        atts = null;
                        state = AFTER_TAG_END;
                        continue;
                    }
                    
                case ' ':
                    if ( state == AFTER_TAG_START ) {
                        if ( elementName == null )
                            elementName = buf.toString();
                        buf = new StringBuffer();
                        state = IN_ATTRIBUTE_NAME;
                        continue;
                    }
                    
                case '=':
                    if ( state == IN_ATTRIBUTE_NAME ) {
                        attributeName = buf.toString();
                        state = BEFORE_ATTRIBUTE_VALUE_START;
                        continue;
                    }
                    
                case '"':
                    if ( state == BEFORE_ATTRIBUTE_VALUE_START ) {
                        buf = new StringBuffer();
                        state = IN_ATTRIBUTE_VALUE;
                        continue;
                    }
                    else if ( ch == '"' && state == IN_ATTRIBUTE_VALUE) {
                        Name name = new Name(attributeName);
                        atts.addAttribute(name.getUri(), name.getLocalname(), name.getQname(), "CDATA", buf.toString() );
                        buf = new StringBuffer();
                        state = AFTER_TAG_START;
                    }
                    
                default:
                    buf.append( ch );
            }
        }
        getContentHandler().endDocument();
    }

    public void setContentHandler(org.xml.sax.ContentHandler contentHandler) {
        this.contentHandler = contentHandler;
    }
    
    public static void main( String[] arguments ) throws Exception {
        XmlReader reader = new XmlReader();
        reader.setContentHandler( new PrintHandler(System.out));
        reader.addPureInlineElement("img");
        reader.parse(new InputSource(new FileInputStream("D:/Sources/maven/nodes/web/src/test/resources/offset/nodes/server/test/page.html")));
    }
    
}
